Índice
- 1. Objetivo
- 2. Alcance
- 3. Entorno y prerrequisitos
- 4. Parámetros de configuración
- 5. Flujo end-to-end
- 6. Diseño de prompts (IA)
- 7. Validaciones e inserción
- 8. Deduplicación
- 9. Logging y resiliencia
- 10. Salida/UX del script
- 11. Seguridad
- 12. Criterios de aceptación
- 13. Plan de pruebas
- 14. Operación y mantenimiento
- 15. Anexos: estructuras y supuestos
1) Objetivo
Construir un script dashboar_faq.php
que, dado curso_id
por GET, lea el campo manual
(HTML) desde cursos_sence
, genere al menos $cant_pre pares Pregunta/Respuesta (P/R) con ayuda de Gemini y los inserte inmediatamente en faq_preguntas
, minimizando el número de llamadas a la API y el impacto al servidor.
2) Alcance
- Lectura de
manual
(HTML) desde BD para uncurso_id
. - Normalización básica del HTML a texto estructurado (titulares/listas).
- Plan de generación con índice temático + producción en lotes (JSONL) para alcanzar
$cant_pre
P/R. - Validación, deduplicación (sin cambios de esquema), inserción inmediata y presentación de lo insertado.
- Registro en log, manejo defensivo de errores y backoff ante 429/5xx.
3) Entorno y prerrequisitos
3.1 Includes existentes
- Conexión MySQL:
include("include/conecta_mysql_otec.php")
→ objeto$mysqli
. - Gemini Keys/Modelo:
include('include/key_geminis.inc')
con:if (function_exists('opcache_reset')) { opcache_reset(); } define('GEMINI_MODEL', 'gemini-2.5-flash'); define('GOOGLE_AI_API_KEY', 'clave KEY'); define('GOOGLE_AI_API_URL', 'https://generativelanguage.googleapis.com/v1beta/models/' . GEMINI_MODEL . ':generateContent?key=' . GOOGLE_AI_API_KEY); define('IA_LOG_FILE', __DIR__ . '/peticiones_ia.log');
- Directorio: todos los archivos del proyecto residen en el mismo directorio que
dashboar_faq.php
.
3.2 Tablas implicadas
cursos_sence
: contiene campomanual
(HTML) asociado al curso.faq_preguntas
: tabla de destino para P/R.
Ver supuestos de campos en Anexo.
4) Parámetros de configuración
$cant_pre
— cantidad objetivo de P/R a insertar (por defecto: 60).$cant_car_pre
— límite de caracteres para pregunta (por defecto: 140).$cant_car_res
— límite de caracteres para respuesta (por defecto: 700).$chunk_len
— tamaño objetivo por bloque de texto del manual tras limpieza (sugerido: 8–12k caracteres).$lote_qas
— Q/A deseadas por llamada de generación (sugerido: 15–20).$max_llamadas
— presupuesto de llamadas a la API (sugerido: 4–5; 1 para índice + 3–4 para Q/A).$sleep_seg
— pausa entre llamadas (sugerido: 2–3 s; backoff dinámico en 429/5xx).
5) Flujo end-to-end
- Entrada:
- Parámetros vía
$_GET
:curso_id
(int obligatorio). - Parámetros de configuración (sección 4) con valores por defecto si no provienen de GET.
- Parámetros vía
- Lectura del manual:
- Consulta:
SELECT manual FROM cursos_sence WHERE id = ?
. Si no existe o vacío, finalizar con mensaje claro y log. - El valor de
manual
es HTML.
- Consulta:
- Limpieza/Normalización HTML:
- Eliminar
<script>
,<style>
, atributos peligrosos. - Convertir a texto estructurado:
- H1/H2/H3 → líneas con prefijos
#
,##
,###
. - Listas → prefijos
-
- Conservar títulos y listas para guiar a la IA.
- H1/H2/H3 → líneas con prefijos
- Partir en chunks de ~
$chunk_len
respetando límites naturales (cortes en H2/H3 idealmente). - Generar un extracto-esqueleto inicial (titulares y párrafos intro) para la llamada 1.
- Eliminar
- Llamada 1 (Índice/Mapa temático):
- Objetivo: 6–8 temas con subtemas y
qas_deseadas
por subtema que sumen ~$cant_pre
+ 10%. - Entrada: extracto del manual + reglas de salida JSON (ver Prompts).
- Salida: JSON con
temas[]
/subtemas[]
/total_qas_sugeridas
.
- Objetivo: 6–8 temas con subtemas y
- Llamadas 2..N (Generación de Q/A en lotes):
- Agrupar 2–3 subtemas por request y pedir ~
$lote_qas
Q/A por llamada (JSONL). - Para cada request, adjuntar solo los chunks relevantes.
- Iterar hasta alcanzar
$cant_pre
o agotar subtemas.
- Agrupar 2–3 subtemas por request y pedir ~
- Parseo y validación:
- Leer JSONL línea a línea. Ignorar vacías. Validar presencia de
pregunta
,respuesta
. - Aplicar límites de longitud según
$cant_car_pre
y$cant_car_res
(recortar si es seguro o descartar). - Normalizar y preparar para deduplicación/inserción.
- Leer JSONL línea a línea. Ignorar vacías. Validar presencia de
- Deduplicación (sin cambiar esquema):
- Cargar set de preguntas existentes (normalizadas) para el
curso_id
actual. - Descartar entradas nuevas cuya pregunta normalizada ya exista en BD o se repita en el lote actual.
- Cargar set de preguntas existentes (normalizadas) para el
- Inserción inmediata:
- Determinar
orden_base = COALESCE(MAX(orden),0)
para el curso. - Insertar por lotes (por ejemplo, 10–25 filas) en transacción.
- Campos/valores:
curso_id
: el recibido por GET.pregunta
,respuesta
: desde IA (limpias).categoria
,palabras_clave
: propuestos por IA (si faltan, usar tema/subtema).orden
: incremental desdeorden_base + 1
.estado
:activo
.fecha_creacion
:NOW()
.
- Guardar los IDs insertados de esta corrida para mostrarlos luego.
- Determinar
- Corte por objetivo:
- Detener cuando se alcance
$cant_pre
P/R válidas insertadas. - Si el último lote sobrepasa, insertar solo lo necesario para completar la meta.
- Detener cuando se alcance
- Salida:
- Resumen de la ejecución.
- Listado (tabla) con las P/R recién insertadas (IDs de esta ejecución), incluyendo
orden
,categoria
,palabras_clave
.
6) Diseño de prompts (IA)
6.1 Instrucción de sistema (común)
Eres un asistente experto en construir FAQs basadas exclusivamente en el contenido proporcionado. Si una respuesta no está sustentada por el documento, omítela. Respeta estrictamente el formato solicitado (JSON o JSONL) sin agregar texto adicional ni comentarios.
6.2 Llamada 1 — Índice/Mapa temático (salida JSON)
Objetivo: Devolver un plan de cobertura que sume ~$cant_pre
(+10%) Q/A.
INPUT: - Extracto del manual (HTML ya simplificado a texto con títulos/listas). - Cantidad objetivo aproximada: $cant_pre (+10% margen). - Reglas: • 6–8 temas principales. • Cada tema con subtemas. Cada subtema con "qas_deseadas" (número). • Evitar redundancias. Priorizar secciones más densas/relevantes. • Salida estricta JSON con la forma: { "temas": [ { "nombre": "Tema 1", "subtemas": [ {"nombre": "Subtema 1.1", "qas_deseadas": 5}, {"nombre": "Subtema 1.2", "qas_deseadas": 4} ] } ], "total_qas_sugeridas": 64 } OUTPUT: - Solo JSON válido como el ejemplo de arriba.
6.3 Llamadas 2..N — Generación de Q/A (salida JSONL)
Objetivo: Generar ~$lote_qas
Q/A por llamada hasta completar $cant_pre
.
INPUT: - Subconjunto del índice (ej. 2–3 subtemas objetivo en esta llamada). - Chunks del manual relevantes a esos subtemas (HTML ya limpio). - Límites: • pregunta: ≤ $cant_car_pre caracteres (clara, única). • respuesta: ≤ $cant_car_res caracteres (precisa, con pasos si aplica). - Reglas: • Sin redundancias respecto a Q/A previas. • Omitir Q/A si el manual no sustenta la respuesta. • Salida estricta JSONL (una línea por objeto), sin encabezados/epílogos, con: { "pregunta": "¿...?", "respuesta": "Texto fundamentado en el manual ...", "categoria": "Tema/Subtema", "palabras_clave": "término1, término2, término3" } OUTPUT: - Solo JSONL (una Q/A por línea).
7) Validaciones e inserción
- Validación de estructura: cada línea JSONL debe contener
pregunta
yrespuesta
(no vacías). - Longitudes: truncar o descartar respetando
$cant_car_pre
y$cant_car_res
(preferir truncar con cuidado semántico; si se pierde sentido, descartar). - Sanitización básica: quitar comillas extra, espacios repetidos, normalizar saltos de línea; preservar acentos (UTF-8).
- Inserción por lotes en transacción: 10–25 filas por batch. Ante fallo, rollback del batch.
- Orden: iniciar en
MAX(orden)+1
delcurso_id
y aumentar de 1 en 1. - Estado:
activo
. Publicación inmediata.
8) Deduplicación
No se modifica el esquema. Deduplicación en aplicación:
- Antes de insertar, cargar
pregunta
de BD para elcurso_id
y normalizar (lowercase, sin tildes, trim, colapsar espacios). - Normalizar cada pregunta nueva y:
- Si ya existe en el set de BD → descartar.
- Si ya existe en el lote actual → descartar.
Opcional (no requerido): registro de las descartadas por duplicado en log para auditoría.
9) Logging y resiliencia
- Archivo:
IA_LOG_FILE
(ya definido en include). - Por cada ejecución: curso_id, tamaños de prompts y respuestas, nº de Q/A válidas, nº descartadas (duplicado/validación), nº llamadas, duración total.
- Errores: registrar código y cuerpo de respuesta de la API si hay 4xx/5xx.
- Backoff: ante 429/5xx, reintentos con espera exponencial (p. ej., 2s, 4s, 8s; máximo 3 intentos por llamada).
- Rate suave: pausa
$sleep_seg
entre llamadas incluso sin error.
10) Salida/UX del script
- Resumen ejecutivo: curso_id, $cant_pre, llamadas usadas, Q/A pedidas, insertadas, descartadas por duplicado/validación, tiempo total.
- Listado de nuevas FAQs (solo las insertadas en esta corrida): tabla con columnas
ID
,orden
,pregunta
,categoria
,palabras_clave
.
Nota: se recomienda no listar respuestas completas si resultan muy extensas; mostrar recorte y ofrecer “ver más” si se decide en la implementación.
11) Seguridad
- Entrada confiable:
curso_id
debe castearse a entero y validarse existencia del curso. - SQL seguro: consultas preparadas con parámetros, charset
utf8mb4
. - Salida HTML: escapar contenido al mostrar (evitar XSS) si se renderiza en navegador.
- Claves: no exponer
GOOGLE_AI_API_KEY
en la salida ni logs públicos.
12) Criterios de aceptación
- Con
?curso_id=<id válido>
y manual no vacío, el script:- Realiza ≤
$max_llamadas
llamadas a la API (1 índice + 2–4 generación), - Inserta inmediatamente ≥
$cant_pre
P/R no duplicadas, - Marca
estado='activo'
y seteaorden
continuo, - Muestra el resumen y la tabla de las P/R insertadas (IDs de esta corrida).
- Realiza ≤
- Si hay contenido insuficiente en el manual, inserta tantas P/R válidas como sea sustentable y lo refleja en el resumen.
- No se producen duplicados por
pregunta
dentro del mismocurso_id
. - La ejecución deja trazas en
peticiones_ia.log
con métricas básicas.
13) Plan de pruebas
Casos positivos
- Curso con manual extenso → genera e inserta ≥
$cant_pre
. - Curso con manual medio → genera varias llamadas y alcanza el objetivo.
- Re-ejecución sobre el mismo curso → no inserta duplicados; continúa
orden
a partir del máximo existente.
Casos negativos / borde
curso_id
inexistente → mensaje claro, sin llamadas a la IA.- Manual vacío → mensaje claro, sin llamadas a la IA.
- Respuesta IA con líneas malformadas → descartar esas líneas, continuar con el resto.
- 429/5xx de API → reintentos con backoff; si excedido, terminar con resumen parcial.
14) Operación y mantenimiento
- Parámetros ajustables:
$cant_pre
,$cant_car_pre
,$cant_car_res
,$chunk_len
,$lote_qas
,$max_llamadas
,$sleep_seg
. - Monitoreo: revisar
IA_LOG_FILE
periódicamente; alertar si hay muchos fallos/429. - Actualizaciones: si cambia el modelo o endpoint, basta con editar
include/key_geminis.inc
.
15) Anexos: estructuras y supuestos
15.1 Estructura esperada de faq_preguntas
(según dump compartido)
Campos previstos (si difieren en tu BD real, mapear durante la implementación):
id
(PK, autoincrement)curso_id
(int)pregunta
(text / varchar)respuesta
(text)categoria
(varchar)palabras_clave
(varchar / text corto, separado por comas)orden
(int)estado
(varchar; usaractivo
)fecha_creacion
(datetime; usarNOW()
)
15.2 Reglas de normalización para deduplicación
- pregunta_norm = lowercase → quitar tildes → colapsar espacios → trim.
- Comparar pregunta_norm contra:
- Set de preguntas existentes (mismo curso) normalizadas.
- Set de preguntas generadas en el lote actual normalizadas.
15.3 Políticas de truncado
- Pregunta: si excede
$cant_car_pre
pero es recortable sin perder sentido, truncar y añadir «…»; si pierde sentido, descartar. - Respuesta: truncar respetando clausulas/ideas completas cuando sea posible; si el recorte invalida la respuesta, descartar.
15.4 Estrategia para minimizar llamadas
- 1ª llamada: índice (plan de cobertura).
- 2–4ª llamadas: generación de Q/A en lotes de ~
$lote_qas
(15–20) hasta completar$cant_pre
. - Enviar a IA solo los chunks relevantes por subtemas en cada llamada.
15.5 Presentación final
- Resumen con métricas de la corrida.
- Tabla con las nuevas FAQs insertadas (IDs de esta ejecución) mostrando
orden
,categoria
,palabras_clave
y un recorte depregunta
/respuesta
.
Este documento describe completamente el comportamiento esperado para implementar dashboar_faq.php
sin requerir consultas adicionales. No incluye código ejecutable; provee especificaciones, reglas y formatos de entrada/salida para desarrollo inmediato.